package vip.ola.code.ctrl;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import vip.ola.code.common.ctrl.BaseController;
import vip.ola.code.common.service.RpcCommonService;
import vip.ola.code.secruity.JwtTokenUtil;
import vip.ola.code.service.UserService;
import vip.ola.core.common.Exception.ServiceException;
import vip.ola.core.common.annotation.MethodLog;
import vip.ola.core.common.constant.Constant;
import vip.ola.core.common.constant.MchConstant;
import vip.ola.core.common.constant.RetEnum;
import vip.ola.core.common.domain.BizResponse;
import vip.ola.core.common.domain.PayResponse;
import vip.ola.core.common.util.CookieUtil;
import vip.ola.core.common.util.MD5Util;
import vip.ola.core.common.util.RandomValidateCodeUtil;
import vip.ola.core.common.util.StrUtil;
import vip.ola.core.entity.AgentInfo;
import vip.ola.core.entity.code.CodeUser;

import javax.imageio.ImageIO;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static vip.ola.core.common.constant.CacheKey.*;
import static vip.ola.core.common.constant.CodeConstant.CODE_STATUS_OK;
import static vip.ola.core.common.constant.CodeConstant.SECURITY_TYPE_PASS_GOOGLE;
import static vip.ola.core.common.constant.Constant.LOGIN_VERIFY_CODE;
import static vip.ola.core.common.constant.Constant.MGR_AUTH_SECRET;
import static vip.ola.core.common.constant.RetEnum.*;

@RequestMapping(Constant.CODE_CONTROLLER_ROOT_PATH + "/auth")
@RestController
public class AuthController extends BaseController {

    @Value("${jwt.cookie}")
    private String tokenCookie;
    @Value("${jwt.expiration}")
    private Integer expiration;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private RpcCommonService rpcService;
    @Autowired
    private UserService userService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 登录鉴权
     *
     * @return
     * @throws AuthenticationException
     */
    @RequestMapping(value = "/login")
    @MethodLog(remark = "登录")
    public ResponseEntity<?> auth() throws AuthenticationException {
        String username = getStringRequired("username");
        String password = getStringRequired("password");
        String imgCode = getStringRequired("imgCode");
        Object sessionVerifyCodeObj = request.getSession().getAttribute(LOGIN_VERIFY_CODE);
        String sessionVerifyCode;
        if (Objects.isNull(sessionVerifyCodeObj)) {
            return ResponseEntity.ok(BizResponse.build(RET_COMM_VERIFY_CODE_ERROR));
        }
        sessionVerifyCode = sessionVerifyCodeObj.toString();
        request.getSession().removeAttribute(LOGIN_VERIFY_CODE);
        if (!StringUtils.equalsIgnoreCase(imgCode, sessionVerifyCode)) {
            return ResponseEntity.ok(BizResponse.build(RET_COMM_VERIFY_CODE_ERROR));
        }
        String token;
        try {
            token = userService.login(username, password);
        } catch (ServiceException e) {
            return ResponseEntity.ok(BizResponse.build(e.getRetEnum()));
        }
        CodeUser user = rpcService.codeUserService.getByName(username);
        JSONObject data = new JSONObject();
        if (SECURITY_TYPE_PASS_GOOGLE.equals(user.getLoginSecurityType())) {
            request.getSession().setAttribute("CODE_TOKEN" + user.getId(), token);
            data.put("userId", user.getId());
            data.put("googleAuth", 1);
        } else {
            data.put("access_token", token);
            stringRedisTemplate.opsForValue().set(CODE_TOKEN + token, "1", TOKEN_TIMEOUT, TimeUnit.SECONDS);
        }
        return ResponseEntity.ok(PayResponse.buildSuccess(data));
    }

    /**
     * google验证
     *
     * @return
     * @throws AuthenticationException
     */
    @RequestMapping(value = "/google_auth")
    public ResponseEntity<?> authGoogle(){
        Long userId = getLongRequired("userId");
        String googleCodeStr = getStringRequired("googleCode").trim();
        long googleCode;
        try {
            googleCode = Long.parseLong(googleCodeStr);
        } catch (Exception e) {
            return ResponseEntity.ok(BizResponse.build(RetEnum.RET_MCH_GOOGLECODE_NOT_MATCH));
        }
        // 判断商户
        CodeUser codeUser = rpcService.codeUserService.getById(userId);
        if (codeUser == null) {
            return ResponseEntity.ok(BizResponse.build(RetEnum.CODE_USER_NOT_EXIST));
        }
        if (CODE_STATUS_OK != codeUser.getStatus()) {
            return ResponseEntity.ok(BizResponse.build(RetEnum.CODE_USER_STATUS_STOP));
        }
        // 验证谷歌验证码
        boolean checkResult = checkGoogleCode(codeUser.getGoogleAuthSecret(), googleCode);
        if (!checkResult) {
            return ResponseEntity.ok(BizResponse.build(RetEnum.RET_MCH_GOOGLECODE_NOT_MATCH));
        }
        Object token = request.getSession().getAttribute("CODE_TOKEN" + codeUser.getId());
        request.getSession().removeAttribute("CODE_TOKEN" + codeUser.getId());
        stringRedisTemplate.opsForValue().set(CODE_TOKEN + token, "1", TOKEN_TIMEOUT, TimeUnit.SECONDS);
        JSONObject data = new JSONObject();
        data.put("access_token", token);
        return ResponseEntity.ok(PayResponse.buildSuccess(data));
    }

    /**
     * 刷新token
     *
     * @param request
     * @return
     * @throws AuthenticationException
     */
    @RequestMapping(value = "/refresh")
    public ResponseEntity<?> refreshAndGetAuthenticationToken(
            HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String token = CookieUtil.getCookieByName(request, tokenCookie);
        String refreshedToken;
        try {
            refreshedToken = userService.refreshToken(token);
        } catch (ServiceException e) {
            return ResponseEntity.ok(BizResponse.build(e.getRetEnum()));
        }
        if (refreshedToken == null) {
            return ResponseEntity.ok(BizResponse.build(RetEnum.RET_COMM_OPERATION_FAIL));
        } else {
            JSONObject data = new JSONObject();
            data.put("token", token);
            // 添加cookie
            Cookie cookie = new Cookie("token", token);
            cookie.setPath("/");
            cookie.setDomain("vip.ola");
            cookie.setMaxAge(expiration);
            response.addCookie(cookie);
            return ResponseEntity.ok(PayResponse.buildSuccess(data));
        }
    }

    /**
     * 修改当前用户登录密码
     *
     * @return
     */
    @RequestMapping("/pwd_update")
    @MethodLog(remark = "修改密码")
    public ResponseEntity<?> updatePassword(HttpServletRequest request) {
        // 旧密码
        String oldRawPassword = getStringRequired("oldPassWord");
        // 新密码
        String rawPassword = getStringRequired("passWord");
        // 验证旧密码是否正确
        CodeUser codeUser = rpcService.codeUserService.getById(getUser().getId());
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (!encoder.matches(oldRawPassword, codeUser.getPassword())) {
            return ResponseEntity.ok(BizResponse.build(RetEnum.RET_MCH_OLDPASSWORD_NOT_MATCH));
        }
        // 判断新密码格式
        if (!StrUtil.checkPassword(rawPassword)) {
            return ResponseEntity.ok(BizResponse.build(RetEnum.RET_MCH_PASSWORD_FORMAT_FAIL));
        }
        codeUser = new CodeUser();
        codeUser.setId(getUser().getId());
        codeUser.setPassword(encoder.encode(rawPassword));
        codeUser.setLastPasswordResetTime(LocalDateTime.now());
        boolean isSuc = rpcService.codeUserService.updateById(codeUser);
        if (isSuc) {
            ResponseEntity.ok(PayResponse.build(RetEnum.RET_COMM_OPERATION_FAIL));
        }
        return ResponseEntity.ok(BizResponse.buildSuccess());
    }

    /**
     * 查询当前用户信息
     *
     * @return
     */
    @RequestMapping("/current")
    public ResponseEntity<?> current() {
        CodeUser sysUser = rpcService.codeUserService.getById(getUser().getId());
        return ResponseEntity.ok(PayResponse.buildSuccess(sysUser));
    }

    /**
     * 登录鉴权(运营平台登录商户系统鉴权)
     *
     * @return
     * @throws AuthenticationException
     */
    @RequestMapping(value = "/mgr_auth")
    public ResponseEntity<?> mgrAuthToken() throws AuthenticationException {
        Long userId = getLongRequired("userId");
        String token = getStringRequired("token");
        CodeUser codeUser = rpcService.codeUserService.getById(userId);
        if (codeUser == null) {
            return ResponseEntity.ok(BizResponse.build(CODE_USER_NOT_EXIST));
        }
        if (!CODE_STATUS_OK.equals(codeUser.getStatus())) {
            return ResponseEntity.ok(BizResponse.build(CODE_USER_STATUS_STOP));
        }
        // 先校验运营平台传过来的token,是否合法
        // 将商户ID+商户密码+密钥 做32位MD5加密转大写
        String password = codeUser.getPassword();
        String rawToken = userId + password + MGR_AUTH_SECRET;
        String myToken = MD5Util.string2MD5(rawToken).toUpperCase();
        if (!myToken.equals(token)) {
            return ResponseEntity.ok(BizResponse.build(RetEnum.RET_MCH_ILLEGAL_LOGIN));
        }
        // 生成jwtToken返回
        String jwtToken = jwtTokenUtil.generateToken(userId, codeUser.getUsername());
        stringRedisTemplate.opsForValue().set(CODE_TOKEN + jwtToken, "1", TOKEN_TIMEOUT, TimeUnit.SECONDS);
        JSONObject data = new JSONObject();
        data.put("access_token", jwtToken);
        return ResponseEntity.ok(PayResponse.buildSuccess(data));
    }

    /**
     * 获取验证码
     */
    @RequestMapping(value = "/code_get")
    public void getAuthCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Map randomMap = RandomValidateCodeUtil.getRandcode(120, 40, 6, 20);
        String randomString = randomMap.get("randomString").toString();
        request.getSession().setAttribute(LOGIN_VERIFY_CODE, randomString);
        BufferedImage randomImage = (BufferedImage) randomMap.get("randomImage");
        ImageIO.write(randomImage, "JPEG", response.getOutputStream());
    }

}
